﻿//*** Guidance generated code ***//
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;

namespace ProjectBase.Core
{
    /// <summary>
    ///     Extensions available on every place of the framework.
    /// </summary>
    public static partial class Ext
    {
        #region TryParse
        /// <summary>
        ///     Tries to bind valueTypes decimal, int, long, short, bool, Guid, DateTime
        ///     from provided string and format provider (if null CultureInfo.CurrentCulture is used)
        /// </summary>
        /// <typeparam name = "T"></typeparam>
        /// <param name = "value"></param>
        /// <param name = "result"></param>
        /// <param name = "culture">Default value is CultureInfo.CurrentCulture</param>
        /// <returns></returns>
        public static bool TryParse<T>(this string value, out T result, IFormatProvider culture = null)
            where T : struct
        {
            ValueType obj;
            result = default(T);
            if (value.TryParse(culture ?? CultureInfo.CurrentCulture, typeof(T), out obj))
            {
                result = (T)obj;
                return true;
            }
            return false;
        }

        /// <summary>
        ///     Only Decimal, Integer, Long, Short, Guid, DateTime are handled by this method (until now)
        ///     Values with parenthis as minus sign, thousands separators etc. are correctly transformed
        ///     into asked ValueTypes using provided culture or "User's current culture".
        ///     This method is the MVC independent (can be used elsewhere)
        ///     <param name = "type">result type (e.g. decimal)</param>
        ///     <param name = "value">string value to be converted</param>
        ///     <param name = "result">result of intended type</param>
        ///     <returns>true if conversion succeeds</returns>
        /// </summary>
        public static bool TryParse(this string value, IFormatProvider culture, Type type, out ValueType result)
        {
            Contract.Requires(type.Is(), " Type parameter was not provided (null)");
            result = 0;

            if (culture.IsNull())
            {
                culture = CultureInfo.CurrentCulture;
            }

            if (value.IsEmpty()
                || value.Trim().IsEmpty()
                || value.IsEqual("null"))
            {
                if (type.IsGenericType) // try to convert result to null
                {
                    result = null;
                    return true;
                }
                return false;
            }

            // I. simple decimal result
            if (type.IsEquivalentTo(typeof(decimal))
                || type.IsEquivalentTo(typeof(decimal?)))
            {
                decimal converted;
                if (decimal.TryParse(value, NumberStyles.Any, culture, out converted))
                {
                    result = converted;
                    return true;
                }
                return false;
            }

            // II. simple double result
            if (type.IsEquivalentTo(typeof(double))
                || type.IsEquivalentTo(typeof(double?)))
            {
                double converted;
                if (double.TryParse(value, NumberStyles.Any, culture, out converted))
                {
                    result = converted;
                    return true;
                }
                return false;
            }

            // III. simple float result
            if (type.IsEquivalentTo(typeof(float))
                || type.IsEquivalentTo(typeof(float?)))
            {
                float converted;
                if (float.TryParse(value, NumberStyles.Any, culture, out converted))
                {
                    result = converted;
                    return true;
                }
                return false;
            }

            // IV. simple int result
            if (type.IsEquivalentTo(typeof(int))
                || type.IsEquivalentTo(typeof(int?)))
            {
                int converted;
                if (int.TryParse(value, NumberStyles.Any, culture, out converted))
                {
                    result = converted;
                    return true;
                }
                return false;
            }

            // V. simple short result
            if (type.IsEquivalentTo(typeof(short))
                || type.IsEquivalentTo(typeof(short?)))
            {
                short converted;
                if (short.TryParse(value, NumberStyles.Any, culture, out converted))
                {
                    result = converted;
                    return true;
                }
                return false;
            }

            // VI. simple short result
            if (type.IsEquivalentTo(typeof(long))
                || type.IsEquivalentTo(typeof(long?)))
            {
                long converted;
                if (long.TryParse(value, NumberStyles.Any, culture, out converted))
                {
                    result = converted;
                    return true;
                }
                return false;
            }

            // VII. bool result
            if (type.IsEquivalentTo(typeof(bool))
                || type.IsEquivalentTo(typeof(bool?)))
            {
                result = false;
                bool converted;
                if (bool.TryParse(value, out converted)) // simple string conversion
                {
                    result = converted;
                }
                else // special handling for html checkboxes, xml bool etc.
                {
                    result = value
                        .Split(',') // is there any piece of information looking like true?
                        .Where(s => s.IsEqual("1")
                                    || s.IsEqual("on")
                                    || s.IsEqual("true")
                                    || (bool.TryParse(s, out converted) && converted)
                        ).Any();
                }
                return true;
            }

            // VIII. guid result
            if (type.IsEquivalentTo(typeof(Guid))
                || type.IsEquivalentTo(typeof(Guid?)))
            {
                try
                {
                    var converted = new Guid(value);
                    result = converted;
                    return true;
                }
                catch (FormatException ex)
                {
                    PublishingManager.Publish(null, "Try to create Guid from string '{0}' failed".FormatWith(value), ex);
                }
                return false;
            }

            // IX. simple uint result
            if (type.IsEquivalentTo(typeof(uint))
                || type.IsEquivalentTo(typeof(uint?)))
            {
                uint converted;
                if (uint.TryParse(value, NumberStyles.Any, culture, out converted))
                {
                    result = converted;
                    return true;
                }
                return false;
            }

            // X. simple ushort result
            if (type.IsEquivalentTo(typeof(ushort))
                || type.IsEquivalentTo(typeof(ushort?)))
            {
                ushort converted;
                if (ushort.TryParse(value, NumberStyles.Any, culture, out converted))
                {
                    result = converted;
                    return true;
                }
                return false;
            }

            // XI. simple ulong result
            if (type.IsEquivalentTo(typeof(ulong))
                || type.IsEquivalentTo(typeof(ulong?)))
            {
                ulong converted;
                if (ulong.TryParse(value, NumberStyles.Any, culture, out converted))
                {
                    result = converted;
                    return true;
                }
                return false;
            }

            // XII. simple byte result
            if (type.IsEquivalentTo(typeof(byte))
                || type.IsEquivalentTo(typeof(byte?)))
            {
                byte converted;
                if (byte.TryParse(value, NumberStyles.Any, culture, out converted))
                {
                    result = converted;
                    return true;
                }
                return false;
            }

            // XIII. simple sbyte result
            if (type.IsEquivalentTo(typeof(sbyte))
                || type.IsEquivalentTo(typeof(sbyte?)))
            {
                sbyte converted;
                if (sbyte.TryParse(value, NumberStyles.Any, culture, out converted))
                {
                    result = converted;
                    return true;
                }
                return false;
            }

            // XIV. simple date result
            if (type.IsEquivalentTo(typeof(DateTime))
                || type.IsEquivalentTo(typeof(DateTime?)))
            {
                DateTime date;
                if (DateTime.TryParse(value, culture, DateTimeStyles.None, out date))
                {
                    result = date;
                    return true;
                }
                double doubleDate;
                if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out doubleDate))
                {
                    result = DateTime.FromOADate(doubleDate);
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        ///     Tries to convert string into the datetime using provided format (e.g. "dd/MM/yy")
        /// </summary>
        /// <param name = "value">not null nor empty string</param>
        /// <param name = "culture">culture, if null current is used</param>
        /// <param name = "result">successfully converted datetime or the default dateTime</param>
        /// <param name = "format">format of the date time, if null "G" is supplied</param>
        /// <returns>false if conversion is not successful</returns>
        public static bool TryParse(this string value, IFormatProvider culture, string format, out DateTime result)
        {
            if (value.Is()
                &&
                DateTime.TryParseExact(value, format ?? "G", culture ?? CultureInfo.CurrentCulture, DateTimeStyles.None,
                    out result))
            {
                return true;
            }
            result = default(DateTime);
            return false;
        }
        #endregion TryParse
    }
}